Skip to main content

05 · Claude Agent SDK:把一个成品 Agent 开放成 SDK

仓库anthropics/claude-agent-sdk-python
Star7.9k(TypeScript 版 1.7k)
版本Python claude-agent-sdk 0.2.139 / npm @anthropic-ai/claude-agent-sdk 0.3.234
语言Python / TypeScript
许可证MIT
层级Harness 脚手架
一句话不是「一个框架」,而是「Claude Code 那个 Agent 的运行时,开放给你编程调用」

一、它和其他框架的根本区别

前面四个框架都是**「给你零件,你组装一个 Agent」。Claude Agent SDK 是「这里有一个已经做好的、非常强的 Agent,你来配置它、扩展它、拦截它」**。

体现在一个很不寻常的实现细节上:

Claude Code CLI 被打包进了 SDK 里 —— 装 claude-agent-sdk 会连 CLI 一起装上,SDK 默认驱动这个内置 CLI 跑。

# ClaudeAgentOptions(cli_path="/path/to/claude") 可以指定用系统里另一份

这意味着你拿到的不是一堆抽象,而是一个进程 —— 一个已经在几百万次真实编码任务里磨过的 Agent Loop。 好处是它的规划能力、工具使用能力、上下文管理立刻就是产品级的;代价是你对内部循环的控制远不如 LangGraph


二、两种调用方式

query():一次性任务

import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

async def main():
# query() 返回的是一个异步迭代器:Agent 每产生一点动静就吐一条消息出来,
# 包括「模型说话」「调用了工具」「工具返回了什么」「本次运行结束」等等
async for message in query(prompt="What is 2 + 2?"):
# 我们只关心模型说的话,所以过滤出 AssistantMessage
if isinstance(message, AssistantMessage):
# 一条消息里可能混着文本块、工具调用块,这里只取文本块
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)

anyio.run(main) # 这个 SDK 是异步优先的,用 anyio 或 asyncio 起

带配置:

options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant", # 追加你自己的系统提示词
max_turns=1, # 最多来回几轮,防跑飞烧钱
cwd="/path/to/project", # Agent 的工作目录,文件工具以此为基准
)

ClaudeSDKClient:双向交互

query() 是「问一句,流式收结果」;ClaudeSDKClient 支持来回对话,而且只有它才能用自定义工具和 hooks

# ClaudeSDKClient 是长连接:一次连上可以来回对话多轮,
# 而且只有它支持自定义工具和 hooks(query() 不支持)
async with ClaudeSDKClient(options=options) as client:
await client.query("Greet Alice") # 发一条消息给 Agent
async for msg in client.receive_response(): # 收这一轮的全部响应
print(msg)
# 这里还可以接着 client.query(...) 发第二轮,上下文是连续的

三、工具:默认就有一整套

默认情况下,Claude 拥有完整的 Claude Code 工具集 —— Read、Write、Edit、Bash、Glob、Grep、WebSearch 等等。这和其他框架「你不给工具它就什么都不会」是相反的默认值。

allowed_tools 不是你以为的白名单

这是最容易踩的坑,官方 README 专门强调:

allowed_tools权限预批准列表:列在里面的工具自动放行;没列的工具并不会被移除,而是落到 permission_modecan_use_tool 去决定。

options = ClaudeAgentOptions(
# ⚠️ allowed_tools 不是白名单,是「免审批名单」:
# 列出的工具直接放行;没列出的工具不会消失,只是要走下面的 permission_mode 决策
allowed_tools=["Read", "Write", "Bash"],
# 权限模式:acceptEdits = 文件编辑类操作自动接受,其余按默认规则
permission_mode="acceptEdits",
# ✅ 想真正禁掉某个工具,必须用 disallowed_tools
disallowed_tools=["WebSearch"],
)

要屏蔽某个工具,必须用 disallowed_toolsallowed_tools 当作「只能用这几个」来理解,会写出一个权限比你以为的宽得多的 Agent。

自定义工具 = 进程内 MCP Server

这是它的一个漂亮设计:自定义工具不是特殊机制,就是一个跑在你自己进程里的 MCP server。

from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient

# @tool(工具名, 给模型看的描述, 参数 schema)
@tool("greet", "Greet a user", {"name": str})
async def greet_user(args):
# 返回值要符合 MCP 的内容块格式,不是随便返回一个字符串
return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]}

# 把工具打包成一个「跑在当前 Python 进程里」的 MCP server —— 不用起子进程
server = create_sdk_mcp_server(name="my-tools", version="1.0.0", tools=[greet_user])

options = ClaudeAgentOptions(
mcp_servers={"tools": server}, # 挂载,键名 "tools" 会成为工具名前缀
# 工具全名规则:mcp__<挂载名>__<工具名>。
# 写进 allowed_tools 是为了免审批,不是为了「让它可用」
allowed_tools=["mcp__tools__greet"],
)

相比起独立进程的 MCP server,进程内版本的好处很直接:不用管子进程、没有 IPC 开销、单进程部署、同进程调试、类型安全。而且两种可以混用:

options = ClaudeAgentOptions(
mcp_servers={
# 进程内:你自己用 @tool 写的工具,零 IPC 开销、好调试
"internal": sdk_server,
# 子进程:社区现成的 MCP server,通过标准输入输出通信
"external": {"type": "stdio", "command": "external-server"},
}
)
# 两种可以混用,对模型来说都只是「一堆可调用的工具」

这是本专题里对 MCP 集成得最彻底的一个框架 —— 别的框架是「支持接 MCP 工具」,它是「自定义工具本身就是 MCP」。


四、Hooks:应用层的确定性拦截

Hook 的定义很讲究 —— 它是 Claude Code 应用(不是 Claude 模型) 在 Agent Loop 的特定点上调用的函数。

from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher

# Hook 是「应用」在工具执行前后调用的函数,不是模型调用的。
# 它的判断是确定性的代码逻辑,模型无权绕过。
async def check_bash_command(input_data, tool_use_id, context):
# input_data 里有模型这次想调的工具名和参数
if input_data["tool_name"] != "Bash":
return {} # 返回空字典 = 不干预,放行
command = input_data["tool_input"].get("command", "")
for pattern in ["foo.sh"]: # 你的黑名单规则
if pattern in command:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny", # 拒绝执行
# 拒绝原因会回传给模型,让它知道为什么失败、换个做法
"permissionDecisionReason": f"Command contains invalid pattern: {pattern}",
}
}
return {}

options = ClaudeAgentOptions(
allowed_tools=["Bash"],
# PreToolUse = 工具执行前触发;matcher="Bash" 表示只对 Bash 工具生效
hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[check_bash_command])]},
)
# 效果:不管模型怎么想,含 foo.sh 的命令一律执行不了。
# 这就是「边界在代码层强制,不靠提示词让模型自律」。

这段代码在做的事是:不管模型怎么想,包含 foo.sh 的 bash 命令一律拒绝。 这就是 01 提到的核心安全原则 —— 边界在代码层强制,不靠提示词让模型自律

LangChain 的 Middleware 对比:

Claude Agent SDK HooksLangChain Middleware
挂在哪应用运行时的事件点(PreToolUse 等)Agent Loop 的图节点缝隙
能做什么允许 / 拒绝 / 反馈改状态、改请求、重试、短路、跳转
心智「拦截器」「洋葱圈中间件」

五、它从 Claude Code 继承的能力

这些不是 SDK 自己实现的,是它驱动的那个运行时自带的:

能力说明
Subagents.claude/agents/ 下的子智能体定义,上下文隔离委派
Skills按需加载的可复用技能包
Slash commands自定义命令
CLAUDE.md项目级持久指令
Todo / 规划内置任务清单管理
上下文压缩自动 compact
权限模式default / acceptEdits / plan / bypassPermissions

这份清单和 DeepAgents 的能力清单高度重合 —— 因为 DeepAgents 的自述就是「Inspired by Claude Code」。区别在于一个是复刻并开放,一个是原厂开放。


六、和 DeepAgents 的正面对比

Claude Agent SDKDeepAgents
Star7.9k27.9k
本质开放一个成品 Agent 运行时用 LangGraph 复刻一套 Harness
模型Claude 为主,换模型能力衰减明显任意(含开源 / 本地 / vLLM)
实现内嵌 CLI 子进程纯 Python 库
工具默认值默认全套工具默认文件系统 + 规划,其余自己给
自定义工具进程内 MCP server普通 Python 函数
拦截机制Hooks(PreToolUse 等)LangChain Middleware
持久化会话由运行时管理LangGraph checkpointer,可换 Postgres
HITL权限模式 + hooks 决策interrupt_on,状态落盘可跨进程恢复
可观测性Anthropic 侧LangSmith / OTel
最强的时候你用 Claude,且要最强的编码 / 长任务能力你要换模型、要自己控持久化和拓扑

一句话:要能力上限选 Claude Agent SDK,要可控性和可移植性选 DeepAgents。


七、供应商中立性:本专题里绑定最紧的一个

必须直说:这个 SDK 是围绕 Claude 设计的。 它的系统提示词、工具集、上下文管理策略都是针对 Claude 的能力特性调过的。

部分换模型的代价
Agent Loop 与工具集高 —— 提示词与工具设计针对 Claude 调优
长任务规划能力高 —— 这正是 Claude 的强项
Hooks / MCP 集成低 —— 机制本身是通用的

这不是缺点,是定位:它不假装中立。 相比之下,OpenAI Agents SDK 宣称支持 100+ 模型但核心能力仍绑 OpenAI,反而更容易让人误判。


八、什么时候用 / 什么时候别用

用它,如果

  • 你要做编码 Agent / 代码库自动化 —— 这是它的主场,没有对手
  • 你的主力模型就是 Claude
  • 你要最快拿到一个「能干活」的 Agent —— 别的框架你要花时间设计工具集和提示词,它已经调好了
  • 你要把成品 Agent 嵌进自己的应用 —— 而不是从零搭一个
  • 你需要在应用层做确定性安全拦截 —— Hooks 机制干净好用

别用它,如果

  • 要模型中立 —— 见上一节
  • 要自定义 Agent Loop 拓扑 —— 循环在 CLI 运行时里,你改不了;要改用 LangGraph
  • 不能接受子进程架构 —— 有些部署环境(受限容器、Serverless)跑内嵌 CLI 会别扭
  • 需要把执行状态存进自己的数据库并跨进程恢复 —— 用 LangGraph 的 checkpointer
  • 任务很短很简单 —— 一个 FAQ 机器人不需要一整套编码 Agent 的工具集